package com.ifonly.datatables.sql;


import com.ifonly.datatables.DTRequest;
import com.ifonly.datatables.DTUtils;
import com.ifonly.datatables.model.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * . 查询SQL封装
 * </p>
 *
 * @author Jerry Ou
 * @version 1.0 2015-08-31 22:55
 * @since JDK 1.6
 */
public class DTSelectSql extends DTSql {

    /**
     * 封装SQL用到的 DTRequest 对象
     */
    private DTRequest dtRequest;
    /**
     * 是否原始SQL标识
     */
    private boolean original;
    /**
     * 生成的 SELECT 子句
     */
    private String select;
    /**
     * 生成的 WHERE 子句
     */
    private String where;
    /**
     * 生成的 ORDER 子句
     */
    private String order;
    /**
     * 原始SQL参数
     */
    private List<Object> params;
    /**
     * 非原始SQL参数
     */
    private Map<String, Object> paramValue;

    /**
     * 构造函数
     * @param dtRequest DTRequest 对象
     */
    public DTSelectSql(DTRequest dtRequest) {
        this(dtRequest, true);
    }

    /**
     * 构造函数
     * @param dtRequest DTRequest 对象
     * @param original 原始SQL标记
     */
    public DTSelectSql(DTRequest dtRequest, boolean original) {
        this.dtRequest = dtRequest;
        this.original = original;
    }

    /**
     * 初始化方法，用于初始化 SQL 子句，以及查询条件封装
     *
     *  segment --> 初始化时候需要传入的查询表 SQL 片段
     *          如 FROM user
     *
     * @param segment 查询表 SQL 片段
     * @param keyword 当segment SQL 中包含where的时候，最终生成sql时候第一个关键字
     * @return 封装的SQL对象
     */
    public DTSql init(String segment, String keyword) {
        initSelectClause();
        initWhereClause(segment, keyword);
        initOrderClause();
        return this;
    }

    /**
     * 初始化 SELECT 子句
     *
     *  如果获取到 DTColumn 列表，则将列表数据拼接成SELECT 子句
     *  如果没有获取 DTColumn 列表， 则查询 *
     *
     */
    private void initSelectClause() {
        List<DTColumn> columns = dtRequest.getColumns();

        if (columns != null && !columns.isEmpty()) {
            StringBuilder sb = new StringBuilder(DTSql.SELECT + " ");
            for (DTColumn column : columns) {
                if (column != null) {
                    boolean empty = DTUtils.strNullOrEmpty(column.getData());

                    if (!empty) {
                        if (DTUtils.strNullOrEmpty(column.getName())) {
                            sb.append(column.getData());
                        } else {
                            sb.append(column.getName()).append(" ").append(DTSql.AS).append(" ").append(column.getData());
                        }
                        sb.append(", ");
                    }
                }

            }
            //在拼接的时候在最后都 添加了 ", " ，最终生成的时候将这个去掉
            this.select = sb.toString().substring(0, sb.lastIndexOf(", "));
        } else {
            this.select = "*";
        }
    }

    /**
     * 初始化 WHERE 子句
     *
     * @param segment 查询表 SQL 片段
     * @param keyword 当segment SQL 中包含where的时候，最终生成sql时候第一个关键字
     */
    private void initWhereClause(String segment, String keyword) {
        boolean c_filter = dtRequest.isC_filter();

        this.where = segment;
        this.params = new ArrayList<Object>();
        this.paramValue = new HashMap<String, Object>();

        if (c_filter) {
            List<Filter> filters = dtRequest.getFilters();
            if (filters != null && !filters.isEmpty()) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0, l = filters.size(); i < l; i++) {
                    Filter filter = filters.get(i);
                    Condition condition = filter.getCondition();
                    switch (condition) {
                        case BETWEEN:
                            if (original) {
                                sb.append(filter.getName());
                                sb.append(condition.condition);
                                this.params.add(filter.getValue());
                                this.params.add(filter.getValue2());
                            } else {
                                sb.append(filter.getName());
                                sb.append(condition.condition
                                        .replace("?", "#{" + filter.getName().replaceAll("\\.", "_") + "_b}")
                                        .replace("?", "#{"+filter.getName().replaceAll("\\.", "_")+"_e}"));
                                this.paramValue.put(filter.getName().replaceAll("\\.", "_") + "_b", filter.getValue());
                                this.paramValue.put(filter.getName().replaceAll("\\.", "_") + "_e", filter.getValue2());
                            }

                            break;
                        case INN:
                        case IN:
                            sb.append(filter.getName());
                            this.params.add(filter.getValue());
                            break;
                        default:
                            if (original) {
                                sb.append(filter.getName());
                                sb.append(condition.condition);
                                this.params.add(filter.getValue());
                            } else {
                                sb.append(filter.getName());
                                sb.append(condition.condition
                                        .replace("?", "#{" + filter.getName().replaceAll("\\.", "_") + "}"));
                                this.paramValue.put(filter.getName().replaceAll("\\.", "_"), filter.getValue());
                            }

                    }
                    if (i + 1 != l) {
                        sb.append(" ").append(DTSql.AND).append(" ");
                    }
                }
                if (segment.contains(DTSql.WHERE)) {
                    this.where = this.where + " " + keyword + " " + sb.toString();
                } else {
                    this.where = this.where + " " + DTSql.WHERE + " " + sb.toString();
                }
            }
        } else {
            DTSearch search = dtRequest.getSearch();
            List<DTColumn> columns = dtRequest.getColumns();

            if (search != null && !DTUtils.strNullOrEmpty(search.getValue())) {
                StringBuilder sb = new StringBuilder();
                for (DTColumn column : columns) {
                    if (column != null) {
                        if (column.isSearchable()) {
                            boolean empty = DTUtils.strNullOrEmpty(column.getData());

                            if (!empty) {
                                String cn;
                                if (DTUtils.strNullOrEmpty(column.getName())) {
                                    cn = column.getData();
                                } else {
                                    cn = column.getName();
                                }
                                sb.append(cn);
                                if (cn.contains(".")) {
                                    cn = cn.replaceAll("\\.", "_");
                                }
                                if (original) {
                                    sb.append(" ").append(DTSql.LIKE).append(" ?");
                                    this.params.add(DTUtils.like(search.getValue()));
                                } else {
                                    sb.append(" ").append(DTSql.LIKE).append(" #{").append(cn).append("}");
                                    this.paramValue.put(cn, DTUtils.like(search.getValue()));
                                }

                                sb.append(" ").append(DTSql.OR).append(" ");

                            }
                        }
                    }
                }
                if (segment.contains(DTSql.WHERE)) {
                    this.where = this.where + " " + keyword + " " + sb.toString().substring(0, sb.lastIndexOf(" " + DTSql.OR + " "));
                } else {
                    this.where = this.where + " " + DTSql.WHERE + " " + sb.toString().substring(0, sb.lastIndexOf(" " + DTSql.OR + " "));
                }

            }
        }
    }

    /**
     * 初始化 ORDER 子句
     */
    private void initOrderClause() {
        List<DTOrder> orders = dtRequest.getOrders();

        if (orders != null && !orders.isEmpty()) {
            List<DTColumn> columns = dtRequest.getColumns();

            StringBuilder sb = new StringBuilder();
            for (DTOrder order : orders) {
                if (order != null) {
                    DTColumn column = columns.get(order.getColumn());
                    if (column != null) {
                        if (!column.isOrderable()) {
                            continue;
                        }
                        boolean empty = DTUtils.strNullOrEmpty(column.getData());

                        if (!empty) {
                            if (DTUtils.strNullOrEmpty(column.getName())) {
                                sb.append(column.getData());
                            } else {
                                sb.append(column.getName());
                            }
                            sb.append(" ").append(order.getDir().toUpperCase()).append(", ");
                        }
                    }

                }

            }
            if (sb.length() != 0) {
                this.order = DTSql.ORDER_BY + " " + sb.toString().substring(0, sb.lastIndexOf(", "));
            } else {
                this.order = "";
            }

        }
    }

    @Override
    public String getSql() {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("SELECT CLAUSE ---> " + select);
            LOGGER.debug("WHERE CLAUSE ---> " + where);
            LOGGER.debug("ORDER CLAUSE ---> " + order);

            if (original) {
                LOGGER.debug("PARAMS ---> " + params);
            } else {
                LOGGER.debug("PARAM_VALUE ---> " + paramValue);
            }
        }

        StringBuilder sb = new StringBuilder();
        if (DTUtils.strNullOrEmpty(select) || DTUtils.strNullOrEmpty(where) ) {
            throw new RuntimeException("SELECT Fragment AND WHERE Fragment must be not null or empty");
        }
        sb.append(select).append(" ").append(where);
        if (!DTUtils.strNullOrEmpty(order)) {
            sb.append(" ").append(order);
        }
        return sb.toString();
    }

    @Override
    public List<Object> getParams() {
        return params;
    }

    @Override
    public Map<String, Object> getParamValue() {
        return paramValue;
    }

    @Override
    public boolean isOriginal() {
        return original;
    }
}
